// todo 
//add information about objects like a number of triangles and vertices
// it's better to allocate and keep lods' vectors than to resize them all the time....
// something showing that the program is calculating lod
// add rotation of each object
// documents from ieee to the paper
// 
#define GL_GLEXT_PROTOTYPES

#include "menu.h"

#include <string>
#include <sstream>
#include <cmath>
#include <assert.h>
#include <stdio.h>

#include "font_related.h"
#include "obj_loader.h"
#include "globals.h"
#include "object3d.h"
#include "lods_file_io.h"

#include <iostream>
using std::cout;
using std::cerr;
using std::endl;
using std::flush;

static const int INF = (int)1e10;

struct genLODScreen_mousemode
{
   enum {
      rotate,
      move,
      zoom
   };
};


bool userIsEnteringValue = false;
std::string genLODScreen_obj_filename[2];
int genLODScreen_displayed_obj = 1;
std::string G_lodparam_tcount_str="0";
int G_lodparam_tcount=0;

static const float DEFAULT_LOD_PARAM_TRIANGLES_PERC=0.15f;

//default edge collapsing for disperse cubes alg is half-collapsing
std::string G_lodparam_edge_coll_type_str = 
   lod_alg_type::disp_cubes_opts::edge_coll_type::names[
   lod_alg_type::disp_cubes_opts::edge_coll_type::half];
int G_lodparam_edge_coll_type = lod_alg_type::disp_cubes_opts::edge_coll_type::half;

std::string G_lodparam_fromlevel_str="0";
unsigned G_lodparam_fromlevel=0;
std::string G_lodparam_tolevel_str="1";
unsigned G_lodparam_tolevel=1;

int genLODScreen_actMouseMode = genLODScreen_mousemode::rotate;
bool mouse_mode_onOff = false;
int prev_mouse_x, prev_mouse_y;

//vect G_eyepos(0.f,0.f,0.f);
//vect G_orig_obj_rot(0.f,0.f,0.f);
//GLfloat G_zoom = 1.0f;

//std::pair<float,float> xborders, yborders, zborders; // each one has (min,max)
//vect orig_obj_cent_trans;
std::vector<vect> G_transls;
std::vector<GLfloat> G_zooms;
std::vector<vect> G_rots;

std::vector< object3dv<tvertex> > G_lodbases;
std::vector< std::pair<vect,vect> > G_baseranges;
std::vector< object3dv<vertex> > G_lods;
std::vector<bool> G_visiblelods;
unsigned G_lodscount=0;
unsigned G_displodscount=0;

int G_lodtype = lod_alg_type::dispersed_cubes;
int G_lodparampos=1;

vect G_scenerot(0.0f,0.0f,0.0f);
GLfloat G_scenezoom=1.0f;
vect G_scenetransl(0.0f,0.0f,0.0f);


std::pair<vect,vect> G_obj_pos[7] = 
{ std::pair<vect,vect>(vect(-1.0f,-1.0f,-1.0f),vect(1.0f,1.0f,1.0f)),

  //std::pair<vect,vect>(vect(-1.0f,-0.5f,0.0f),vect(0.0f,0.5f,1.0f)),
  //std::pair<vect,vect>(vect(0.0f,-0.5f,0.0f),vect(1.0f,0.5f,1.0f)),
  std::pair<vect,vect>(vect(-1.0f,-0.5f,-0.5f),vect(0.0f,0.5f,0.5f)),
  std::pair<vect,vect>(vect(0.0f,-0.5f,-0.5f),vect(1.0f,0.5f,0.5)),

  //std::pair<vect,vect>(vect(-1.0f,0.0f,0.0f),vect(0.0f,1.0f,1.0f)),
  //std::pair<vect,vect>(vect(0.0f,0.0f,0.0f),vect(1.0f,1.0f,1.0f)),
  //std::pair<vect,vect>(vect(-1.0f,-1.0f,0.0f),vect(0.0f,0.0f,1.0f)),
  //std::pair<vect,vect>(vect(0.0f,-1.0f,0.0f),vect(1.0f,0.0f,1.0f)),
  std::pair<vect,vect>(vect(-1.0f,0.0f,-0.5f),vect(0.0f,1.0f,0.5f)),
  std::pair<vect,vect>(vect(0.0f,0.0f,-0.5f),vect(1.0f,1.0f,0.5f)),
  std::pair<vect,vect>(vect(-1.0f,-1.0f,-0.5f),vect(0.0f,0.0f,0.5f)),
  std::pair<vect,vect>(vect(0.0f,-1.0f,-0.5f),vect(1.0f,0.0f,0.5f)),
};

static const int MAX_DISPLAYED_LODS=4;
static const int MAX_LODS=16;

unsigned G_active_obj=MAX_DISPLAYED_LODS+2;

int G_obj_pos_idx[] = { 0,
                     0,
                     1,
                     3,3,
                     7,7,7,7};

bool G_main_screen_draw_help = false;
bool G_gen_screen_draw_help = false;
                     
                     
static inline void reset_scene_view(vect& rot, float& zoom, vect& transl);

static inline bool allocate_obj(std::vector< object3dv<vertex> >& lods, 
                     std::vector< std::pair<vect,vect> >& baseranges, 
                     std::vector<vect>& transls, std::vector<GLfloat>& zooms,
                     std::vector<vect>& rots, std::vector<bool>& visiblelods,
                     unsigned lodidx);

static inline void new_disp_lods_count(unsigned& activeobj,int& genLodScreenActMouseMode,
                              vect& scenetransl, float& scenezoom, vect& scenerot)
{
   //set all objects active
   activeobj=MAX_DISPLAYED_LODS+2;
   genLodScreenActMouseMode = genLODScreen_mousemode::rotate;
   reset_scene_view(scenerot,scenezoom,scenetransl);
}


static void makelod(std::vector< object3dv<vertex> >& lods,
      std::vector< std::pair<vect,vect> >& baseranges,
      std::vector< object3dv<tvertex> >& lodbases,
      unsigned fromlevel, unsigned tolevel,
      std::vector<vect>& transls, std::vector<GLfloat>& zooms,
      std::vector<vect>& rots, std::vector<bool>& visiblelods,
      unsigned& lodscount, unsigned& displodscount,
      unsigned& lodparam_fromlevel, std::string& lodparam_fromlevel_str,
      unsigned& lodparam_tolevel, std::string& lodparam_tolevel_str,
      int& genLodScreenActMouseMode,
      vect& scenetransl, float& scenezoom, vect& scenerot,
      unsigned& activeobj,
      int& lodparam_tcount, std::string& lodparam_tcount_str, int lodparam_edgecolltype)
{
   assert(lods.size()>0);
   assert(tolevel<(unsigned)MAX_LODS);
   
   if(tolevel>=lodbases.size()) {
      lodbases.resize(tolevel+1);
   }
   else {
      //todo
      //probably unnecessary, cause we're clearing every lodbase after usage
      lodbases[tolevel].clear();
   }

   assert(fromlevel<lods.size() && lods[fromlevel].vcount>0);
   assert(fromlevel<baseranges.size());

   prepare_lod_base(lods[fromlevel].vects, lods[fromlevel].vcount,
         lods[fromlevel].mats, lods[fromlevel].mcount,
         lods[fromlevel].tris, lods[fromlevel].tcount,
         lodbases[tolevel],
         baseranges[fromlevel].first, baseranges[fromlevel].second);
   

   
   int newvcount;//=lods[fromlevel].vcount;
   int newtcount;//=lods[fromlevel].tcount;
   vect mins(INF,INF,INF);
   vect maxs(-INF,-INF,-INF);
   bool retbool;
   int end;

   if(G_lodtype == lod_alg_type::dispersed_cubes)
   {
      int oldtc=lodbases[tolevel].tcount;
      dispersed_cubes_lod(lodbases,baseranges,fromlevel,tolevel,
         lodparam_tcount, lodparam_edgecolltype, newvcount,newtcount);
          
      cerr<<"del"<<oldtc-newtcount<<std::endl;
      //cout<<newvcount<<endl<<std::flush;
      //cout<<newtcount<<endl<<std::flush;
   }

   retbool=allocate_obj(lods, baseranges, transls, zooms, rots, visiblelods, tolevel);
   if(retbool==false) {
      //it's newly created LOD
      lodscount++;
   }
   clear_obj_data(lodbases[tolevel], lods[tolevel], newvcount, newtcount,mins,maxs);
   baseranges[tolevel]=std::pair<vect,vect>(mins,maxs);

   float xrange = maxs.x-mins.x;
   float yrange = maxs.y-mins.y;
   float zrange = maxs.z-mins.z;
          
   transls[tolevel].x = -(mins.x+(xrange/2));
   transls[tolevel].y = -(mins.y+(yrange/2));
   transls[tolevel].z = -(mins.z+(zrange/2));

   //rots[tolevel].x=0.0f;
   //rots[tolevel].y=0.0f;
   //rots[tolevel].z=0.0f;

   zooms[tolevel]=1.0f;

   lodbases[tolevel].clear();

   end=visiblelods.size();
   for(int i=0; i<end; i++)
      visiblelods[i]=false;
   //display two newly created LODs
   visiblelods[fromlevel]=true;
   visiblelods[tolevel]=true;
   if(fromlevel!=tolevel)
      displodscount=2;
   else
      displodscount=1;

   if(tolevel<MAX_LODS-1)
   {                 //if we reach LOD MAX_LODS-1, we can't suggest making
                     // one more level cause it exceeds the MAX_LODS constraint
      lodparam_fromlevel=tolevel;
      lodparam_tolevel=tolevel+1;
   
      std::ostringstream ostr;
      //ostr<<lodparam_fromlevel;
      //lodparam_fromlevel_str=ostr.str();

      //ostr.str("");
      ostr<<lodparam_tolevel;
      lodparam_tolevel_str=ostr.str();

      //todo
      //lodparam_tcount=(int)(lods[0].tcount * 
       //               lodparam_tolevel * DEFAULT_LOD_PARAM_TRIANGLES_PERC);
      lodparam_tcount= int(lods[0].tcount * DEFAULT_LOD_PARAM_TRIANGLES_PERC);
                      
      if(lodparam_tcount>(lods[0].tcount*0.8f))
         lodparam_tcount=(int)(lods[0].tcount*0.8f);
      ostr.str("");
      ostr<<lodparam_tcount;
      lodparam_tcount_str=ostr.str();
   }

   //genLodScreenActMouseMode = genLODScreen_mousemode::rotate;

   //new_disp_lods_count(lods,baseranges,objpos,objposidx,
    //           visiblelods,displodscount,
     //          transls,zooms,rots, activeobj);
   new_disp_lods_count(activeobj,genLODScreen_actMouseMode,
            scenetransl,scenezoom,scenerot);

   //find first existing object and set rotation 
   //of the newly generated object to the same value
   end=lods.size();
   for(int i=0; i<end; i++) {
      if(((unsigned)i)!=tolevel && lods[i].tcount>0) {
         rots[tolevel].x = rots[i].x;
         rots[tolevel].y = rots[i].y;
         rots[tolevel].z = rots[i].z;
      }
   }
}

static inline void reset_scene_view(vect& rot, float& zoom, vect& transl)
{
   rot.x=0;
   rot.y=0;
   rot.z=0;

   zoom=1.0f;

   transl.x=0.0f;
   transl.y=0.0f;
   transl.z=0.0f;
}

static void gen_screen_help(int width, int height)
{
   int cwidth = int((2.5/5.0)*width);
   int cheight=405;
   DrawString("Mouse movement with L-button clicked changes ", 
         cwidth, cheight);
   cheight-=20;
   DrawString("an active property of the active LOD / entire scene.",
         cwidth, cheight);
   cheight-=20;
   DrawString("z  -  sets rotation the active property",
         cwidth, cheight);
   cheight-=20;
   DrawString("x  -  sets scaling the active property",
         cwidth, cheight);
   cheight-=20;
   DrawString("x  -  sets panning the active property",
         cwidth, cheight);
   cheight-=25;
   DrawString("Shift-<nr>  -  makes LOD displayed at position <nr> active",
         cwidth, cheight);
   cheight-=20;
   DrawString("e.g. Shift-1 sets the first displayed LOD active",
         cwidth, cheight);
   cheight-=25;
   DrawString("a  -  sets all displayed LODS / entire scene active",
         cwidth, cheight);
   cheight-=25;
   DrawString("r  -  resets the view of the whole scene",
         cwidth, cheight);
   cheight-=20;
   DrawString("R  -  resets the view of the active LOD",
         cwidth, cheight);
   cheight-=35;
   DrawString("<nr>  -  marks LOD at level <nr> displayed/hidden",
         cwidth, cheight);
   cheight-=20;
   DrawString("Up to 4 LODs might be displayed.",
         cwidth, cheight);

   cheight-=35;
   DrawString("<nr>  -  marks LOD at level <nr> displayed / hidden",
         cwidth, cheight);
   cheight-=20;
   DrawString("m  -  switches to wireframe/shaded display",
         cwidth, cheight);
   cheight-=20;
   DrawString("M  -  switches between smooth/flat shading",
         cwidth, cheight);
   cheight-=20;
   DrawString("v  -  turns VBO on/off (not working yet!)",
         cwidth, cheight);
   cheight-=20;
   DrawString("escape  -  goes to main screen menu",
         cwidth, cheight);
}

static void main_screen_help(int width, int height)
{
   int cheight=45;
   DrawString("At every screen, pressing 'h' will give you ", 
         int((3.0/5.0)*width), cheight);
   cheight-=20;
   DrawString("a list of binded keys.",
         int(3.0/5.0*width), cheight);
}


void display_main_menu(int width, int height, int chosenpos)
{
    int i=1;
    PushOrtho(width, height); 
    //glDisable(GL_LIGHTING);
    G_lighting.disable();

    setMenuColor();
    if(i++==chosenpos)
       setActMenuColor();
    DrawString("Load & show LOD", width/3, height*2/3);

    setMenuColor();
    if(i++==chosenpos)
       setActMenuColor();
    DrawString("Generate LOD", width/3, height*2/3-30);

    setMenuColor();
    if(i++==chosenpos)
       setActMenuColor();
    DrawString("Performance test", width/3, height*2/3-60);

    setMenuColor();
    if(i++==chosenpos)
       setActMenuColor();
    DrawString("Quit", width/3, height*2/3-90);

    //setActMenuColor();
    setHelpTextColor();
    if(G_main_screen_draw_help)
       main_screen_help(width,height);
    else
       DrawString("Press 'h' for help.", int(4.0/5.0*width), 20);
    
    PopOrtho();
    //G_lighting.enable();
    //glEnable(GL_LIGHTING);
}

void main_menu_keyboard(unsigned char key, int x, int y,int& actMenuPos)
{
   switch(key)
   {
      case 'w':
         if(actMenuPos==1)
            actMenuPos=main_menu_positions::pos_count; // jump to the last position
         else
            actMenuPos--;
         break;
      case 's':
         if(actMenuPos==main_menu_positions::pos_count)
            actMenuPos=1;
         else
            actMenuPos++;
         break;
      case 'h':
         G_main_screen_draw_help = !G_main_screen_draw_help;
         break;
   }
}

static void load_orig_obj_to_vbo(std::vector<vertex>& verts, int verts_count, 
        std::vector<triangle>& triangles, int trcount)
{
   glBindBufferARB(GL_ARRAY_BUFFER_ARB, G_vboid_v);
   //glBufferDataARB(GL_ARRAY_BUFFER_ARB, verts_count*sizeof(tvertex), &verts[0], GL_STATIC_DRAW_ARB);
   glBufferDataARB(GL_ARRAY_BUFFER_ARB, verts_count*3*sizeof(float), NULL, GL_STATIC_DRAW_ARB);
   for(int i=0; i<verts_count; i++)
       glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, i*3*sizeof(float), 3*sizeof(float), &verts[i]);
   glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);

   glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, G_vboid_i);
   glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, trcount*sizeof(triangle), &triangles[0], GL_STATIC_DRAW_ARB);
   glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
}

// todo
// sometimes the color of the menu is changed, although it's not necesarry
void display_generateLODScreen(int width, int height, int chosenpos)
{
   int pos_i=1;
   int cheight=height-20;

   PushOrtho(width, height); 
   G_lighting.disable();

   if(pos_i==chosenpos)
      setActMenuColor();
   else
      setMenuColor();
   if(userIsEnteringValue && pos_i==chosenpos)
   {
      char tab[128];
      if(genLODScreen_obj_filename[!genLODScreen_displayed_obj].length()<=110) {
         sprintf(tab,"Load the object: %s", genLODScreen_obj_filename[!genLODScreen_displayed_obj].c_str());
         DrawString(tab, 10, cheight);
      }
   }
   else
      DrawString("Load an object", 10, cheight);
   pos_i++;

   // todo
   // We're checking if there exist at least one object.
   // This knowledge can be used later.
   {
      unsigned end=G_lods.size();
      unsigned i;
      for(i=0; i<end; i++)
         if(G_lods[i].vcount>0) {
            if(pos_i==chosenpos)
               setActMenuColor();
            else
               setMenuColor();

            cheight-=20;
            if(userIsEnteringValue && pos_i==chosenpos) {
                                                               // this time I'm precise
               if(genLODScreen_obj_filename[!genLODScreen_displayed_obj].length()<=41)
               {
                  char msg[64];
                  sprintf(msg, "Save the objects as:  %s", 
                     genLODScreen_obj_filename[!genLODScreen_displayed_obj].c_str());
                  DrawString(msg, 10, cheight);
               }
               else
                  //todo Popraw opisy przy pozostalych bledach
                  //    (wlasciwie to nie ma zadnych).
                  DrawString("Eyy man, too long file name..." , 10, cheight);
            }
            else {
               DrawString("Save the objects", 10, cheight);
            }
            break;
         }
      pos_i++;
   }

   setMenuColor();
   cheight-=30;
   DrawString("Simplification method:", 10, cheight);

   if(pos_i++==chosenpos)
      setActMenuColor();
   else
      setMenuColor();
   cheight-=20;
   DrawString(lod_alg_type::names[G_lodtype], 30, cheight);

   
   setMenuColor();
   cheight-=20;
   DrawString("Generation parameters:", 10, cheight);

   switch(G_lodtype) {
      case lod_alg_type::dispersed_cubes:
      {
         int pos=1;
         if(pos_i==chosenpos && G_lodparampos==pos)
            setActMenuColor();
         else
            setMenuColor();
         cheight-=20;
         if(userIsEnteringValue && pos_i==chosenpos && G_lodparampos==pos) {
            char tab[64];
            if(genLODScreen_obj_filename[!genLODScreen_displayed_obj].length()<45) {
               sprintf(tab,"triangles: %s", genLODScreen_obj_filename[!genLODScreen_displayed_obj].c_str());
               DrawString(tab, 30, cheight);
            }
         }
         else {
            //todo forbid to input BIG numbers in the input function
            if(G_lodparam_tcount_str.size()>0 && G_lodparam_tcount_str.size()<45) {
               char tab[64];
               sprintf(tab,"triangles: %s", G_lodparam_tcount_str.c_str());
               DrawString(tab, 30, cheight);
            }
            else
               DrawString("triangles:", 30, cheight);
         }
         pos++;

         if(pos_i==chosenpos && G_lodparampos==pos++)
            setActMenuColor();
         else
            setMenuColor();
         cheight-=20;
         if(G_lodparam_edge_coll_type_str.size()<40) {
            char tab[64];
            sprintf(tab,"edge collapse type: %s", G_lodparam_edge_coll_type_str.c_str());
            DrawString(tab, 30, cheight);
         }
         break;
      }
      case lod_alg_type::random:
         break;
      case lod_alg_type::angle:
         break;
   }
   pos_i++;


   if(G_lodscount>0)
   {
      if(pos_i==chosenpos)
         setActMenuColor();
      else
         setMenuColor();
      cheight-=30;
      if(userIsEnteringValue && pos_i==chosenpos)
      {
         char tab[32];
         if(genLODScreen_obj_filename[!genLODScreen_displayed_obj].length()<15) {
            sprintf(tab,"From level: %s", genLODScreen_obj_filename[!genLODScreen_displayed_obj].c_str());
            DrawString(tab, 10, cheight);
         }
      }
      else
      {
         assert(G_lodparam_fromlevel_str.size()>0);
         if(G_lodparam_fromlevel_str.size()<10) {
            char tab[32];
            sprintf(tab,"From level: %s", G_lodparam_fromlevel_str.c_str());
            DrawString(tab, 10, cheight);
         }
      }
      pos_i++;


      if(pos_i==chosenpos)
         setActMenuColor();
      else
         setMenuColor();
      cheight-=20;
      if(userIsEnteringValue && pos_i==chosenpos)
      {
         char tab[32];
         if(genLODScreen_obj_filename[!genLODScreen_displayed_obj].length()<15) {
            sprintf(tab,"To level: %s", genLODScreen_obj_filename[!genLODScreen_displayed_obj].c_str());
            DrawString(tab, 10, cheight);
         }
      }
      else
      {
         assert(G_lodparam_tolevel_str.size()>0);
         if(G_lodparam_tolevel_str.size()<10) {
            char tab[32];
            sprintf(tab,"To level: %s", G_lodparam_tolevel_str.c_str());
            DrawString(tab, 10, cheight);
         }
      }
      pos_i++;

      //generate button
      if(pos_i++==chosenpos)
         setActMenuColor();
      else
         setMenuColor();
      cheight-=30;
      DrawString("Generate", 10, cheight);

      if(G_lodscount>1)//omit delete button if there's only one LOD
      {
         //delete button
         if(pos_i==chosenpos)
            setActMenuColor();
         else
            setMenuColor();
         cheight-=20;
         if(userIsEnteringValue && pos_i==chosenpos)
         {
            char tab[32];
            if(genLODScreen_obj_filename[!genLODScreen_displayed_obj].length()<15) {
               sprintf(tab,"Delete: %s", genLODScreen_obj_filename[!genLODScreen_displayed_obj].c_str());
               DrawString(tab, 10, cheight);
            }
         }
         else
         {
            DrawString("Delete: ", 10, cheight);
         }
      }
      pos_i++;
   }
   else
   {
      if(pos_i++==chosenpos)
         setActMenuColor();
      else
         setMenuColor();
      cheight-=30;
      DrawString("From level:", 10, cheight);

      if(pos_i++==chosenpos)
         setActMenuColor();
      else
         setMenuColor();
      cheight-=20;
      DrawString("To level:", 10, cheight);

      pos_i++; //there're no lods loaded, thus omit generation and delete button
      pos_i++;
   }
   

   if(G_lodscount>0)
   {
      cheight-=30;
      char tab[32];
      
      // draw details about visible LODs
      setMenuColor();
      int end=G_visiblelods.size();
      bool atleastone=false;
      for(int i=0; i<end; i++) 
      {
         //assert(G_lods[i].vcount>0);
         //assert(G_lods[i].tcount>0);
                                 // todo
                                 // glupie, takie sprawdzenie powinno byc w innym miejscu
                                 // against tab overflow
         if(G_lods[i].vcount>0 && G_visiblelods[i] && 
            i<1000 && G_lods[i].vcount<1e20 && G_lods[i].tcount<1e20)
         {
            atleastone=true;
            cheight-=20;
            sprintf(tab, "LOD %i", i);
            DrawString(tab, 10, cheight);

            cheight-=20;
            sprintf(tab, "vertices: %i", G_lods[i].vcount);
            DrawString(tab, 20, cheight);

            cheight-=20;
            sprintf(tab,"triangles: %i", G_lods[i].tcount);
            DrawString(tab, 20, cheight);
         }
      }

      if(atleastone)
         cheight-=30;

      // draw a list of all accessible LODs
      // mark the visibles
      int xpos=10;
      for(int i=0; i<end; i++)
      {
         if(G_lods[i].vcount>0 && 
            // to be coherent with the list of details
            i<1000 && G_lods[i].vcount<1e20 && G_lods[i].tcount<1e20)
         if(G_visiblelods[i])
            setVisibleLODColor();
         else
            setInvisibleLODColor();
         cheight-=20;
         sprintf(tab, "%i", i);
         DrawString(tab, xpos, cheight);
         
         if(i%2==1) {
            xpos+=40;
            cheight+=40;
         }
      }
   }
   

   if(genLODScreen_obj_filename[genLODScreen_displayed_obj].size()<55) {
      char tab[64];
      sprintf(tab, "Object: %s", genLODScreen_obj_filename[genLODScreen_displayed_obj].c_str());
      setMenuColor();
      DrawString(tab, width/5, 10);
   }

   if(G_gen_screen_draw_help) {
      //setActMenuColor();
      setHelpTextColor();
      gen_screen_help(width,height);
   }

   PopOrtho();
   G_lighting.enable();


   if(G_lodscount>0) {
      //draws bounding cubes
      {
         G_lighting.disable();
         glMatrixMode(GL_MODELVIEW);
         glPushMatrix();
         glTranslatef(0.f,0.f,-2.f);
         glScalef(G_scenezoom,G_scenezoom,G_scenezoom);
         glRotatef(G_scenerot.x,1.0f,0.0f,0.0f);
         glRotatef(G_scenerot.y,0.0f,1.0f,0.0f);
         glTranslatef(G_scenetransl.x,G_scenetransl.y,G_scenetransl.z);

         int sti=G_obj_pos_idx[G_displodscount];
         for(unsigned i=0; i<G_displodscount; i++) {
            vect& min=G_obj_pos[sti+i].first;
            vect& max=G_obj_pos[sti+i].second;

            //front face
            glColor3f(.25f,.64f,.81f);
            glBegin(GL_LINE_STRIP);
               glVertex3f(min.x,min.y,max.z);
               glVertex3f(min.x,max.y,max.z);
               glVertex3f(max.x,max.y,max.z);
               glVertex3f(max.x,min.y,max.z);
               glVertex3f(min.x,min.y,max.z);
            glEnd();

            glBegin(GL_LINES);
               glColor3f(.25f,.64f,.81f);
               glVertex3f(min.x,min.y,max.z);
               glColor3f(0.98f,0.1f,0.2f);
               glVertex3f(min.x,min.y,min.z);

               glColor3f(.25f,.64f,.81f);
               glVertex3f(min.x,max.y,max.z);
               glColor3f(0.98f,0.1f,0.2f);
               glVertex3f(min.x,max.y,min.z);

               glColor3f(.25f,.64f,.81f);
               glVertex3f(max.x,max.y,max.z);
               glColor3f(0.98f,0.1f,0.2f);
               glVertex3f(max.x,max.y,min.z);

               glColor3f(.25f,.64f,.81f);
               glVertex3f(max.x,min.y,max.z);
               glColor3f(0.98f,0.1f,0.2f);
               glVertex3f(max.x,min.y,min.z);
            glEnd();

            //back face
            glColor3f(0.98f,0.1f,0.2f);
            glBegin(GL_LINE_STRIP);
               glVertex3f(max.x,min.y,min.z);
               glVertex3f(max.x,max.y,min.z);
               glVertex3f(min.x,max.y,min.z);
               glVertex3f(min.x,min.y,min.z);
               glVertex3f(max.x,min.y,min.z);
            glEnd();
         }
         glPopMatrix();
         G_lighting.enable();
      }
      
      if(G_usevbo)
      {
         setVboOrigObjColor();
         glBindBufferARB(GL_ARRAY_BUFFER_ARB, G_vboid_v);
         glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, G_vboid_i);
         glEnableClientState(GL_VERTEX_ARRAY);
           //glVertexPointer(3, GL_FLOAT, 24, 0);
           glVertexPointer(3, GL_FLOAT, 0, 0);
           //glDrawElements(GL_TRIANGLES, 3*G_orig_obj_trcount, GL_UNSIGNED_INT, 0);
           glDrawElements(GL_TRIANGLES, 3*G_lods[0].tcount, GL_UNSIGNED_INT, 0);
         glDisableClientState(GL_VERTEX_ARRAY);
         glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
         glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
      }
      else // not using vertex buffer object
      {
         //setOrigObjColor();
         glMatrixMode(GL_MODELVIEW);
         glPushMatrix();
         glTranslatef(0.f,0.f,-2.f);
         glScalef(G_scenezoom,G_scenezoom,G_scenezoom);
         glRotatef(G_scenerot.x,1.0f,0.0f,0.0f);
         glRotatef(G_scenerot.y,0.0f,1.0f,0.0f);
         glTranslatef(G_scenetransl.x,G_scenetransl.y,G_scenetransl.z);
         

         int activeoi;
         {
            unsigned acto=0;
            unsigned end=G_visiblelods.size();
            unsigned i;
            for(i=0; i<end; i++)
               if(G_lods[i].vcount>0 && G_visiblelods[i]) {
                  acto++;
                  if(acto==G_active_obj)
                     break;
               }
            if(acto==G_active_obj)
               activeoi=i;
            else
               activeoi=G_lods.size();
         }

         

         int sti=G_obj_pos_idx[G_displodscount];
         int end=G_lods.size();
         int vit=0;
         for(int i=0; i<end; i++)
         {
            if(G_lods[i].vcount>0 && G_lods[i].tcount>0 && G_visiblelods[i])
            {
               glLightfv(GL_LIGHT0, GL_POSITION, G_lighting.lights[0].pos.pos);
               //glEnable(GL_NORMALIZE);

               glPushMatrix();
               G_lighting.disable();
               glColor3f(0.0f,1.0f,1.0f);
               glTranslatef(G_lighting.lights[0].pos.pos[0],
                            G_lighting.lights[0].pos.pos[1],
                            G_lighting.lights[0].pos.pos[2]);
               //glutWireCube(0.1);
               glutWireSphere(0.05,6,6);
               G_lighting.enable();
               glPopMatrix();

               //tutaj dodaj etykiety do wyswietlanych obiektow
               //glPushMatrix();
               //G_lighting.disable();
               //glRasterPos3f(0,0,0);
               //glTranslatef(0,0,3);
               //glutStrokeCharacter(GLUT_STROKE_ROMAN, 'a');
               //glutWireCube(1.1);
               //G_lighting.enable();
               //glPopMatrix();

               

               glPushMatrix();
               
               const vect& min=G_obj_pos[sti+vit].first;
               const vect& max=G_obj_pos[sti+vit].second; 
               float toposx=min.x+(max.x-min.x)/2;
               float toposy=min.y+(max.y-min.y)/2;
               float toposz=min.z+(max.z-min.z)/2;

               //we're bounding with cubes, so boundxrange==boundyrange=boundzrange
               float boundxrange=max.x-min.x;

               float xrange=(G_baseranges[i].second.x-G_baseranges[i].first.x);
               float yrange=(G_baseranges[i].second.y-G_baseranges[i].first.y);
               float zrange=(G_baseranges[i].second.z-G_baseranges[i].first.z);
               float maxrange=xrange>yrange ? xrange : yrange;
               maxrange=maxrange>zrange ? maxrange : zrange;
               assert(maxrange!=0);
               //rescale*maxrange=boundxrange
               float rescale=boundxrange/maxrange;

               glTranslatef(toposx,toposy,toposz);

               glRotatef(G_rots[i].x,1.0f,0.0f,0.0f);
               glRotatef(G_rots[i].y,0.0f,1.0f,0.0f);

               //
               //todo
               //glScalef(G_zooms[i],G_zooms[i],G_zooms[i]);
               glScalef(rescale,rescale,rescale);

               glTranslatef(G_transls[i].x,G_transls[i].y,G_transls[i].z);


               if(i==activeoi)
                  setActiveObjColor();
               else
                  setOrigObjColor();

               if(G_lighting.enabled())
               {
                  GLfloat col[4] = {0.2,0.2,0.2,1.0};
                  //glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT,col);
                  //col[0]=0.8;
                  //col[1]=0.8;
                  //col[2]=0.8;
                  //col[3]=1.0;
                  //glMaterialfv(GL_FRONT_AND_BACK,GL_DIFFUSE,col);
                  col[0]=0.8;
                  col[1]=0.8;
                  col[2]=0.8;
                  col[3]=1.0;
                  glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,col);
                  col[0]=0.0;
                  col[1]=0.0;
                  col[2]=0.0;
                  col[3]=1.0;
                  glMaterialfv(GL_FRONT_AND_BACK,GL_EMISSION,col);
                  glMaterialf(GL_FRONT_AND_BACK,GL_SHININESS,50.0);
               }
               
               glBegin(GL_TRIANGLES);
                  int endj=G_lods[i].tcount;
                  vertex* v;
                  
                  // todo maybe it's worth to employ all materials?
                  // for now, set only the first material for the whole object
                  if(G_lighting.enabled() && G_lods[i].mcount>0 && i!=activeoi)
                  {
                     GLfloat mprop[4];
                     mprop[0]=G_lods[i].mats[0].diffuse[0];
                     mprop[1]=G_lods[i].mats[0].diffuse[1];
                     mprop[2]=G_lods[i].mats[0].diffuse[2];
                     mprop[3]=1.0f;
                     //glMaterialfv(GL_FRONT_AND_BACK,GL_DIFFUSE,mprop);
                     glColor4fv(mprop);
                     mprop[0]=G_lods[i].mats[0].specular[0];
                     mprop[1]=G_lods[i].mats[0].specular[1];
                     mprop[2]=G_lods[i].mats[0].specular[2];
                     glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mprop);
                  }
                  for(int j=0; j<endj; j++)
                  {
                     
                     v = &(G_lods[i].vects)[(G_lods[i].tris)[j].verts[0]];
                     glNormal3fv(v->normal);
                     glVertex3fv(v->xyz);
                     v = &(G_lods[i].vects)[(G_lods[i].tris)[j].verts[1]];
                     glNormal3fv(v->normal);
                     glVertex3fv(v->xyz);
                     v = &(G_lods[i].vects)[(G_lods[i].tris)[j].verts[2]];
                     glNormal3fv(v->normal);
                     glVertex3fv(v->xyz);
                  }
               glEnd();

               glPopMatrix();
               vit++;
            }
         }
         glPopMatrix();
         
      }
   }
}

// assures that there's a place at index i in the objects' vector
// the place is being cleared
// the return value says whether another lod was destroyed to make a place for this one
static inline bool allocate_obj(std::vector< object3dv<vertex> >& lods, 
                     std::vector< std::pair<vect,vect> >& baseranges, 
                     std::vector<vect>& transls, std::vector<GLfloat>& zooms,
                     std::vector<vect>& rots, std::vector<bool>& visiblelods,
                     unsigned i)
{
   assert(i>=0);
   if(i>=0 && i<lods.size()) {
      //lods[i].vects.clear();
      //lods[i].tris.clear();
      //lods[i].mats.clear();
      //lods[i].vcount=0;
      //lods[i].mcount=0;
      //lods[i].tcount=0;
      if(lods[i].vcount>0) {
         lods[i].clear();
         return true;
      }
      //leave baseranges[i] as it is
      //also transls,zooms,rots; all of them should be filled before displaying
      //the same with visiblelods
      else {
         assert(lods[i].vects.size()==0 && lods[i].tris.size()==0 && 
               lods[i].mats.size()==0);
         assert(lods[i].vcount<=0 && lods[i].tcount<=0 && lods[i].mcount<=0);
         return false;
      }
   }
   else {//(i>=lods.size()) {
      lods.resize(i+1);
      baseranges.resize(i+1);
      transls.resize(i+1);
      zooms.resize(i+1);
      rots.resize(i+1);
      visiblelods.resize(i+1);
      return false;
   }
}

// if i is a valid index, deletes an object at index i 
// if i equals the number of all objects, deletes all of them
static inline void deallocate_obj(std::vector< object3dv<vertex> >& lods, 
                     std::vector< std::pair<vect,vect> >& baseranges, 
                     std::vector<vect>& transls, std::vector<GLfloat>& zooms,
                     std::vector<vect>& rots, std::vector<bool>& visiblelods,
                     unsigned i)
{
   if(i>=0 && i<lods.size()) {
      lods[i].vects.clear();
      lods[i].tris.clear();
      lods[i].mats.clear();
      lods[i].vcount=0;
      lods[i].mcount=0;
      lods[i].tcount=0;
      //leave baseranges[i] as it is
      //also transls,zooms,rots; all of them should be filled before displaying
      //the same with visiblelods
   }
   else if(i==lods.size()) {
      lods.clear();
      baseranges.clear();
      transls.clear();
      zooms.clear();
      rots.clear();
      visiblelods.clear();
   }
}

/*static void inline allocate_orig_obj()
{
   G_orig_obj_verts = new std::vector<vertex>;
   G_orig_obj_triangles = new std::vector<triangle>;
   G_orig_obj_materials = new std::vector<material>;
}

// todo
// Unallocating could clear() vectors instead of deleting them.
static void inline deallocate_orig_obj()
{
   delete G_orig_obj_verts;
   delete G_orig_obj_triangles;
   delete G_orig_obj_materials;
   G_orig_obj_verts=0;
   G_orig_obj_triangles=0;
   G_orig_obj_materials=0;
   G_orig_obj_vcount=0;
   G_orig_obj_matcount=0;
   G_orig_obj_trcount=0;
}*/

static error loadUnload_orig_obj(std::string filename)
{
   error ret;
   // deletes all of the objects, if any
   deallocate_obj(G_lods,G_baseranges,G_transls,G_zooms,G_rots,G_visiblelods,
            G_lods.size());
   

   if(filename.length()>0) {
      // load a new object
      //allocate_orig_obj();
      allocate_obj(G_lods,G_baseranges,G_transls,G_zooms,G_rots,G_visiblelods,0);
      std::pair<float,float> xborders;
      std::pair<float,float> yborders;
      std::pair<float,float> zborders;
      xborders.first = INF;
      xborders.second = -INF;
      yborders.first = INF;
      yborders.second = -INF;
      zborders.first = INF;
      zborders.second = -INF;
      
      ret = load_gat(filename.c_str(), G_lods[0].vects, G_lods[0].vcount,
               G_lods[0].mats, G_lods[0].mcount,
               G_lods[0].tris, G_lods[0].tcount,
               xborders, yborders, zborders);
      if(ret.code!=error::OK) {
         deallocate_obj(G_lods,G_baseranges,G_transls,G_zooms,G_rots,G_visiblelods,
                        G_lods.size());
         return ret;
      }
      
      (G_baseranges[0]).first.x = xborders.first;
      (G_baseranges[0]).first.y = yborders.first;
      (G_baseranges[0]).first.z = zborders.first;
      (G_baseranges[0]).second.x = xborders.second;
      (G_baseranges[0]).second.y = yborders.second;
      (G_baseranges[0]).second.z = zborders.second;
      //cout<<xborders.first<<" "<<xborders.second<<endl;
      //cout<<yborders.first<<" "<<yborders.second<<endl;
      //cout<<zborders.first<<" "<<zborders.second<<endl;

      G_lodscount=1;
      G_visiblelods[0]=true;
      G_displodscount=1;
      G_lodparam_fromlevel=0;
      G_lodparam_fromlevel_str="0";
      G_lodparam_tolevel=1;
      G_lodparam_tolevel_str="1";

      //set suggested number of triangles to collapse to a value of 
      //x % from triangle count
      G_lodparam_tcount=(int)(G_lods[0].tcount*DEFAULT_LOD_PARAM_TRIANGLES_PERC);
      std::ostringstream ostr;
      ostr<<G_lodparam_tcount;
      G_lodparam_tcount_str=ostr.str();
      

      G_lodparam_edge_coll_type = lod_alg_type::disp_cubes_opts::edge_coll_type::half;
      G_lodparam_edge_coll_type_str = 
            lod_alg_type::disp_cubes_opts::edge_coll_type::names[
            lod_alg_type::disp_cubes_opts::edge_coll_type::half];
      genLODScreen_actMouseMode = genLODScreen_mousemode::rotate;

      //we're bounding with cubes, so boundxrange==boundyrange=boundzrange
      //float boundxrange=G_obj_pos[0].second.x-G_obj_pos[0].first.x;
      //float boundyrange=G_obj_pos[0].second.y-G_obj_pos[0].first.y;
      //float boundzrange=G_obj_pos[0].second.z-G_obj_pos[0].first.z;
      
      float xrange=(xborders.second-xborders.first);
      float yrange=(yborders.second-yborders.first);
      float zrange=(zborders.second-zborders.first);

      //float maxrange=xrange>yrange ? xrange : yrange;
      //maxrange=maxrange>zrange ? maxrange : zrange;
      
      
      //assert(maxrange!=0);
            //zoom*maxrange=boundxrange
      //G_zooms[0]=boundxrange/maxrange;

      //float centx=xborders.first+(xrange/2);
      //float centy=yborders.first+(yrange/2);
      //float centz=zborders.first+(zrange/2);

      //float Ox=G_obj_pos[0].first.x+boundxrange/2;
      //float Oy=G_obj_pos[0].first.y+boundyrange/2;
      //float Oz=G_obj_pos[0].first.z+boundzrange/2;
      
      //G_transls[0].x=Ox-centx;
      //G_transls[0].y=Oy-centy;
      //G_transls[0].z=Oz-centz;
      G_transls[0].x=-(xborders.first+(xrange/2));
      G_transls[0].y=-(yborders.first+(yrange/2));
      G_transls[0].z=-(zborders.first+(zrange/2));

      G_rots[0].x=0.0f;
      G_rots[0].y=0.0f;
      G_rots[0].z=0.0f;

      G_zooms[0]=1.0f;
      
      reset_scene_view(G_scenerot,G_scenezoom,G_scenetransl);
      //G_eyepos.x = 0.f;
      //G_eyepos.y = 0.f;
      //G_eyepos.z = 0.f;
      
   }
   return error(error::OK,0);
}

int generateLODScreen_keyboard(unsigned char key, int x, int y, int& actMenuPos)
{
   error ret;
   if(userIsEnteringValue)
   {
      if(actMenuPos==generateLODScreen_positions::load_obj)
      {
         if(key>='a'&&key<='z' || key>='A'&&key<='Z' || key>='0'&&key<='9' || 
            key==' ' || key=='/' || key=='\\' || key=='.' || key=='_')
            genLODScreen_obj_filename[!genLODScreen_displayed_obj]  += key;
         else if(key==13) { // enter
            userIsEnteringValue=false;
            if(genLODScreen_obj_filename[!genLODScreen_displayed_obj].size()>0) {
               ret = loadUnload_orig_obj(genLODScreen_obj_filename[!genLODScreen_displayed_obj]);
               if(ret.code==error::OK) {
                  //if(G_orig_obj_verts!=0 && G_orig_obj_triangles!=0) {
                  //
                  //todo
                  //don't load to vbo if we're not using vbo
                     load_orig_obj_to_vbo(G_lods[0].vects, G_lods[0].vcount,
                           //*G_orig_obj_triangles, G_orig_obj_trcount);
                           G_lods[0].tris, G_lods[0].tcount);
                     genLODScreen_displayed_obj = !genLODScreen_displayed_obj;
                  //}
                  //else
                   //  genLODScreen_obj_filename[genLODScreen_displayed_obj]="";
               }
               else {
                  genLODScreen_obj_filename[genLODScreen_displayed_obj]="";
                  cout << error::descriptions[ret.code] << " Line " << ret.bad_line << endl;
               }
            }
         }
         else if(key==27) // escape
            //todo 
            //the input has been discarded, so we can clean the changed string
            userIsEnteringValue=false;
         else if(key=='\b') {
            if(genLODScreen_obj_filename[!genLODScreen_displayed_obj].length()>0)
               genLODScreen_obj_filename[!genLODScreen_displayed_obj] = genLODScreen_obj_filename[!genLODScreen_displayed_obj].substr(0,genLODScreen_obj_filename[!genLODScreen_displayed_obj].length()-1);
         }
      }
      else if(actMenuPos==generateLODScreen_positions::save_obj)
      {
         if(key>='a'&&key<='z' || key>='A'&&key<='Z' || key>='0'&&key<='9' || 
            key==' ' || key=='/' || key=='\\' || key=='.' || key=='_')
            genLODScreen_obj_filename[!genLODScreen_displayed_obj]  += key;
         else if(key==13) { // enter
            userIsEnteringValue=false;
            if(genLODScreen_obj_filename[!genLODScreen_displayed_obj].size()>0) {
               std::cerr<<"Saving LODs to the file: "<<genLODScreen_obj_filename[!genLODScreen_displayed_obj]<<endl;
               int ret = save_lods(genLODScreen_obj_filename[!genLODScreen_displayed_obj], G_lods);
               if(ret==0)
                  std::cerr<<"File saved.\n";
               else
                  std::cerr<<"File NOT saved.\n";
            }
         }
         else if(key==27) { // escape
            genLODScreen_obj_filename[!genLODScreen_displayed_obj].clear();
            userIsEnteringValue=false;
         }
         else if(key=='\b') {
            if(genLODScreen_obj_filename[!genLODScreen_displayed_obj].length()>0)
               genLODScreen_obj_filename[!genLODScreen_displayed_obj] = genLODScreen_obj_filename[!genLODScreen_displayed_obj].substr(0,genLODScreen_obj_filename[!genLODScreen_displayed_obj].length()-1);
         }
      }
      else if(actMenuPos==generateLODScreen_positions::gen_params)
      {
         if(key>='0' && key<='9') {
            genLODScreen_obj_filename[!genLODScreen_displayed_obj]  += key;
         }
         else if(key==13) { // enter
            userIsEnteringValue=false;
            if(genLODScreen_obj_filename[!genLODScreen_displayed_obj].size()>0) {
               std::istringstream str(genLODScreen_obj_filename[!genLODScreen_displayed_obj].c_str());
               int tc;
               str>>tc;
               std::ostringstream ostr;
               ostr<<tc;//filter possible zeros from the beginning
               G_lodparam_tcount_str=ostr.str();
               G_lodparam_tcount=tc;
            }
         }
         else if(key==27) { // escape
            userIsEnteringValue=false;
            genLODScreen_obj_filename[!genLODScreen_displayed_obj].clear();
         }
         else if(key=='\b') {
            if(genLODScreen_obj_filename[!genLODScreen_displayed_obj].length()>0)
               genLODScreen_obj_filename[!genLODScreen_displayed_obj] = genLODScreen_obj_filename[!genLODScreen_displayed_obj].substr(0,genLODScreen_obj_filename[!genLODScreen_displayed_obj].length()-1);
         }
      }
      else if(actMenuPos==generateLODScreen_positions::from_level)
      {
         if(key>='0' && key<='9') {
            genLODScreen_obj_filename[!genLODScreen_displayed_obj]  += key;
         }
         else if(key==13) { // enter
            userIsEnteringValue=false;
            if(genLODScreen_obj_filename[!genLODScreen_displayed_obj].size()>0) {
               std::istringstream str(genLODScreen_obj_filename[!genLODScreen_displayed_obj].c_str());
               unsigned fromlevel;
               str>>fromlevel;
               if(fromlevel<G_lods.size() && G_lods[fromlevel].vcount>0) {
                  std::ostringstream ostr;
                  ostr<<fromlevel;//filter possible zeros from the beginning
                  G_lodparam_fromlevel_str=ostr.str();
                  G_lodparam_fromlevel=fromlevel;
               }
            }
         }
         else if(key==27) { // escape
            userIsEnteringValue=false;
            genLODScreen_obj_filename[!genLODScreen_displayed_obj].clear();
         }
         else if(key=='\b') {
            if(genLODScreen_obj_filename[!genLODScreen_displayed_obj].length()>0)
               genLODScreen_obj_filename[!genLODScreen_displayed_obj] = genLODScreen_obj_filename[!genLODScreen_displayed_obj].substr(0,genLODScreen_obj_filename[!genLODScreen_displayed_obj].length()-1);
         }
      }
      else if(actMenuPos==generateLODScreen_positions::to_level)
      {
         if(key>='0' && key<='9') {
            genLODScreen_obj_filename[!genLODScreen_displayed_obj]  += key;
         }
         else if(key==13) { // enter
            userIsEnteringValue=false;
            if(genLODScreen_obj_filename[!genLODScreen_displayed_obj].size()>0) {
               std::istringstream str(genLODScreen_obj_filename[!genLODScreen_displayed_obj].c_str());
               unsigned tolevel;
               str>>tolevel;
               if(tolevel<(unsigned)MAX_LODS) {
                  std::ostringstream ostr;
                  ostr<<tolevel;//filter possible zeros from the beginning
                  G_lodparam_tolevel_str=ostr.str();
                  G_lodparam_tolevel=tolevel;
               }
            }
         }
         else if(key==27) { // escape
            userIsEnteringValue=false;
            genLODScreen_obj_filename[!genLODScreen_displayed_obj].clear();
         }
         else if(key=='\b') {
            if(genLODScreen_obj_filename[!genLODScreen_displayed_obj].length()>0)
               genLODScreen_obj_filename[!genLODScreen_displayed_obj] = genLODScreen_obj_filename[!genLODScreen_displayed_obj].substr(0,genLODScreen_obj_filename[!genLODScreen_displayed_obj].length()-1);
         }
      }
      else if(actMenuPos==generateLODScreen_positions::del)
      {
         if(key>='0' && key<='9') {
            genLODScreen_obj_filename[!genLODScreen_displayed_obj]  += key;
         }
         else if(key==13) { // enter
            userIsEnteringValue=false;
            if(genLODScreen_obj_filename[!genLODScreen_displayed_obj].size()>0) {
               std::istringstream str(genLODScreen_obj_filename[!genLODScreen_displayed_obj].c_str());
               unsigned leveltodel;
               str>>leveltodel;
               if(leveltodel<G_lods.size() && G_lods[leveltodel].vcount>0) {
                  cout<<"Delete LOD "<<leveltodel<<std::endl;
               }
            }
         }
         else if(key==27) { // escape
            userIsEnteringValue=false;
            genLODScreen_obj_filename[!genLODScreen_displayed_obj].clear();
         }
         else if(key=='\b') {
            if(genLODScreen_obj_filename[!genLODScreen_displayed_obj].length()>0)
               genLODScreen_obj_filename[!genLODScreen_displayed_obj] = genLODScreen_obj_filename[!genLODScreen_displayed_obj].substr(0,genLODScreen_obj_filename[!genLODScreen_displayed_obj].length()-1);
         }
      }
   }
   else
   {
      //todo dziury jak cholera w interfejsie
      //    no, moze nie takie straszne jak teraz o tym mysle, ale sa
      unsigned acto;
      bool omitactivation=false;
      switch(key)
      {
         case 'h':
            G_gen_screen_draw_help = !G_gen_screen_draw_help;
            break;
         case '0':
         case '1':
         case '2':
         case '3':
         case '4':
         case '5':
         case '6':
         case '7':
         {
            unsigned i=key-'0';
            if(i<G_visiblelods.size()) {
               if(G_visiblelods[i]) {
                  G_visiblelods[i]=false;
                  G_displodscount--;
               }
               else if(G_displodscount<(unsigned)MAX_DISPLAYED_LODS) {
                  G_visiblelods[i]=true;
                  G_displodscount++;
               }

               //new_disp_lods_count(G_lods,G_baseranges,G_obj_pos,G_obj_pos_idx,
                //     G_visiblelods,G_displodscount,
                 //    G_transls,G_zooms,G_rots, G_active_obj);
               new_disp_lods_count(G_active_obj,genLODScreen_actMouseMode,
                     G_scenetransl,G_scenezoom,G_scenerot);
            }
            break;
         }
         case '!':
            acto=1;
            omitactivation=true;
         case '@':
            if(omitactivation==false) {
               acto=2;
               omitactivation=true;
            }
         case '#':
            if(omitactivation==false) {
               acto=3;
               omitactivation=true;
            }
         case '$':
            if(omitactivation==false) {
               acto=4;
               omitactivation=true;
            }
         case 'a':
            if(omitactivation==false) {
               if(G_active_obj==MAX_DISPLAYED_LODS+2)
                  acto=MAX_DISPLAYED_LODS+1;
               else
                  acto=MAX_DISPLAYED_LODS+2;
               //omitactivation=true;
            }
            //cout<<acto<<endl;
            if(G_displodscount>=acto) {
               if(G_active_obj==acto)
                  G_active_obj=MAX_DISPLAYED_LODS+2;
               else
                  G_active_obj=acto;
            }
            else if(acto==MAX_DISPLAYED_LODS+1 || acto==MAX_DISPLAYED_LODS+2)
               G_active_obj=acto;
            
            break;
         case 'r':
            reset_scene_view(G_scenerot,G_scenezoom,G_scenetransl);
            break;
         case 'R':
         {
            int end=G_lods.size();
            for(int i=0; i<end; i++)
               if(G_lods[i].tcount>0) {
                  G_rots[i].x = 0.0f;
                  G_rots[i].y = 0.0f;
                  G_rots[i].z = 0.0f;
                  G_zooms[i] = 1.0f;
               }
            break;
         }
         case 'w':
         {
            bool leave=false;
            bool was_gen_params=false;
            // nested menu 
            if(actMenuPos == generateLODScreen_positions::gen_params) {
               was_gen_params=true;
               switch(G_lodtype) {
                  case lod_alg_type::dispersed_cubes:
                     if(G_lodparampos==1)
                        //G_lodparampos=lod_alg_type::disp_cubes_opts::count;
                        ;//leave=false;
                     else {
                        G_lodparampos--;
                        leave=true;
                     }
                     break;
                  default:
                     leave=false;
               }
            }
            if(leave==false) {
               if(actMenuPos == 1)
                  actMenuPos = generateLODScreen_positions::pos_count; // jump to the last position
               else
                  actMenuPos--;
            }
            if(actMenuPos == generateLODScreen_positions::gen_params && 
                  was_gen_params==false) {
               switch(G_lodtype) {
                  case lod_alg_type::dispersed_cubes:
                     G_lodparampos=lod_alg_type::disp_cubes_opts::count;
                     break;
               }
            }
            if(G_lodscount<=1) {
               if(actMenuPos == generateLODScreen_positions::del)
                  actMenuPos--;
            }
            if(G_lodscount<=0) {
               if(actMenuPos == generateLODScreen_positions::generate)
                  actMenuPos--;
               else if(actMenuPos == generateLODScreen_positions::save_obj)
                  actMenuPos--;
            }
            break;
         }
         case 's':
         {
            bool leave=false;
            bool was_gen_params=false;
            // nested menu 
            if(actMenuPos == generateLODScreen_positions::gen_params) {
               was_gen_params=true;
               switch(G_lodtype) {
                  case lod_alg_type::dispersed_cubes:
                     if(G_lodparampos==lod_alg_type::disp_cubes_opts::count)
                        //G_lodparampos=1;
                        ;//leave=false;
                     else {
                        G_lodparampos++;
                        leave=true;
                     }
                     break;
                  default:
                     leave=false;
               }
            }
            if(leave==false) {
               actMenuPos++;
            }
            if(actMenuPos == generateLODScreen_positions::gen_params && 
                  was_gen_params==false) {
               G_lodparampos=1;
            }
            if(G_lodscount<=0) {
               if(actMenuPos == generateLODScreen_positions::generate)
                  actMenuPos++;
               else if(actMenuPos == generateLODScreen_positions::save_obj)
                  actMenuPos++;
            }
            if(G_lodscount<=1) {
               if(actMenuPos == generateLODScreen_positions::del)
                  actMenuPos++;
            }
            if(actMenuPos == generateLODScreen_positions::pos_count+1)
               actMenuPos = 1;
            break;
         }
         case 'v':
            G_usevbo = !G_usevbo;
            break;
         case 'z': // view rotation
            genLODScreen_actMouseMode = genLODScreen_mousemode::rotate; 
            mouse_mode_onOff = false;
            break;
         case 'x': // view zoom
            genLODScreen_actMouseMode = genLODScreen_mousemode::zoom; 
            mouse_mode_onOff = false;
            break;
         case 'c': // view pan
            genLODScreen_actMouseMode = genLODScreen_mousemode::move; 
            mouse_mode_onOff = false;
            break;
         case 'm':
            //if(G_filled_or_wired=='f') {
               //G_filled_or_wired='w';
               //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
            //}
            //else {
               //G_filled_or_wired='f';
               //glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
            //}
            if(G_filled_or_wired==GL_FILL)
               G_filled_or_wired=GL_LINE;
            else
               G_filled_or_wired=GL_FILL;
            glPolygonMode(GL_FRONT_AND_BACK, G_filled_or_wired);
            break;
         case 'M':
            if(G_lighting.shade_model==GL_FLAT)
               G_lighting.set_shade_model(GL_SMOOTH);
            else
               G_lighting.set_shade_model(GL_FLAT);
            break;
         case 13: // enter
            if(actMenuPos==generateLODScreen_positions::load_obj)
            {
               userIsEnteringValue=true;
               genLODScreen_obj_filename[!genLODScreen_displayed_obj].clear();
            }
            if(actMenuPos==generateLODScreen_positions::save_obj)
            {
               userIsEnteringValue=true;
               genLODScreen_obj_filename[!genLODScreen_displayed_obj].clear();
            }
            else if(actMenuPos==generateLODScreen_positions::gen_params)
            {
               //disperse cubes 
               if(G_lodparampos==lod_alg_type::disp_cubes_opts::triangles) {
                  userIsEnteringValue=true;
                  genLODScreen_obj_filename[!genLODScreen_displayed_obj].clear();
               }
               else if(G_lodparampos==lod_alg_type::disp_cubes_opts::edge_collapse_type) {
                  if(G_lodparam_edge_coll_type==
                     lod_alg_type::disp_cubes_opts::edge_coll_type::count-1) {
                     G_lodparam_edge_coll_type=0;
                  }
                  else {
                     G_lodparam_edge_coll_type++;
                  }
                  G_lodparam_edge_coll_type_str=
                     lod_alg_type::disp_cubes_opts::edge_coll_type::names[
                     G_lodparam_edge_coll_type];
                  
               }
            }
            else if(actMenuPos==generateLODScreen_positions::alg_type)
            {
               if(G_lodtype==lod_alg_type::count-1)
                  G_lodtype=0;
               else
                  G_lodtype++;
            }
            else if(actMenuPos==generateLODScreen_positions::from_level)
            {
               if(G_lodscount>0) { 
                  userIsEnteringValue=true;
                  genLODScreen_obj_filename[!genLODScreen_displayed_obj].clear();
               }
            }
            else if(actMenuPos==generateLODScreen_positions::to_level)
            {
               if(G_lodscount>0) { 
                  userIsEnteringValue=true;
                  genLODScreen_obj_filename[!genLODScreen_displayed_obj].clear();
               }
            }
            else if(actMenuPos==generateLODScreen_positions::generate)
            {
               cout<<"Fire up gen proc!"<<std::endl;
               makelod(G_lods,G_baseranges,G_lodbases,
                     G_lodparam_fromlevel,G_lodparam_tolevel,
                     G_transls,G_zooms,G_rots,G_visiblelods,
                     G_lodscount,G_displodscount,
                     G_lodparam_fromlevel, G_lodparam_fromlevel_str,
                     G_lodparam_tolevel, G_lodparam_tolevel_str,
                     genLODScreen_actMouseMode,
                     G_scenetransl,G_scenezoom,G_scenerot,
                     G_active_obj,
                     G_lodparam_tcount,G_lodparam_tcount_str, G_lodparam_edge_coll_type);
            }
            else if(actMenuPos==generateLODScreen_positions::del)
            {
               assert(G_lodscount>1);
               userIsEnteringValue=true;
               genLODScreen_obj_filename[!genLODScreen_displayed_obj].clear();
            }
            break;
         case 27: // escape
            return key;
      }
   }
   return 0;
}

void generateLODScreen_mouse(int button, int state, int x, int y)
{
   //if(G_orig_obj_verts!=0 && G_orig_obj_triangles!=0) {
   if(G_lods.size()>0) {
      if(button==GLUT_LEFT_BUTTON) {
         mouse_mode_onOff = !mouse_mode_onOff;
         prev_mouse_x = x;
         prev_mouse_y = y;
      }
   }
}

void generateLODScreen_mouse_motion(int x, int y)
{
   vect changes(0.0f,0.0f,0.0f);

   if(G_lods.size()>0) {
      switch(genLODScreen_actMouseMode)
      {
         case genLODScreen_mousemode::rotate:
            changes.y = (x-prev_mouse_x)*origObjRotOf1Pixel;
            changes.x = (y-prev_mouse_y)*origObjRotOf1Pixel;
            if(changes.x<=30) {
               unsigned acto=0;
               //unsigned end=G_visiblelods.size();
               unsigned end=G_lods.size();
               unsigned i;
               for(i=0; i<end; i++)
                  if(G_lods[i].vcount>0) {// && G_visiblelods[i])
                     acto++;
                     if(acto==G_active_obj)
                        break;
                  }
               if(G_active_obj==acto) {
                  G_rots[i].x += changes.x;
                  if(G_rots[i].x>=360)
                     G_rots[i].x-=360;
                  else if(G_rots[i].x<0)
                     G_rots[i].x+=360;
               }
               else if(G_active_obj==MAX_DISPLAYED_LODS+1) {//rotate entire scene
                  G_scenerot.x += changes.x;
                  if(G_scenerot.x >= 360)
                     G_scenerot.x -= 360;
                  else if(G_scenerot.x < 0)
                     G_scenerot.x += 360;
               }
               else if(G_active_obj==MAX_DISPLAYED_LODS+2) {//rotate all objects
                  for(i=0; i<end; i++) {
                     if(G_lods[i].vcount>0) {// && G_visiblelods[i])
                        G_rots[i].x += changes.x;
                        if(G_rots[i].x>=360)
                           G_rots[i].x-=360;
                        else if(G_rots[i].x<0)
                           G_rots[i].x+=360;
                     }
                  }
               }
            }
            if(changes.y<=30) {
               unsigned acto=0;
               //unsigned end=G_visiblelods.size();
               unsigned end=G_lods.size();
               unsigned i;
               for(i=0; i<end; i++)
                  if(G_lods[i].vcount>0) {// && G_visiblelods[i])
                     acto++;
                     if(acto==G_active_obj)
                        break;
                  }

               if(G_active_obj==acto) {
                  G_rots[i].y+=changes.y;
                  if(G_rots[i].y>=360)
                     G_rots[i].y-=360;
                  else if(G_rots[i].y<0)
                     G_rots[i].y+=360;
               }
               else if(G_active_obj==MAX_DISPLAYED_LODS+1) {//rotate entire scene
                  G_scenerot.y += changes.y;
                  if(G_scenerot.y >= 360)
                     G_scenerot.y -= 360;
                  else if(G_scenerot.y < 0)
                     G_scenerot.y += 360;
               }
               else if(G_active_obj==MAX_DISPLAYED_LODS+2) {
                  for(i=0; i<end; i++) {
                     if(G_lods[i].vcount>0) {// && G_visiblelods[i])
                        G_rots[i].y+=changes.y;
                        if(G_rots[i].y>=360)
                           G_rots[i].y-=360;
                        else if(G_rots[i].y<0)
                           G_rots[i].y+=360;
                     }
                  }
               }
            }
            prev_mouse_x = x;
            prev_mouse_y = y;
            break;
         case genLODScreen_mousemode::zoom:
         {
            float zoom_change = 
               std::sqrt((x-prev_mouse_x)*(x-prev_mouse_x)+
                         (y-prev_mouse_y)*(y-prev_mouse_y));
            zoom_change *= objZoomOf1Pixel;
            if(x-prev_mouse_x>0 && y-prev_mouse_y<0)
               //G_zoom += zoom_change;
               G_scenezoom += zoom_change;
            else if(x-prev_mouse_x<0 && y-prev_mouse_y>0)
               //G_zoom -= zoom_change;
               G_scenezoom -= zoom_change;

            prev_mouse_x = x;
            prev_mouse_y = y;
            break;
         }
         case genLODScreen_mousemode::move:
            changes.x = (x-prev_mouse_x)*objMoveOf1Pixel;
            changes.y = (y-prev_mouse_y)*objMoveOf1Pixel;
            if(changes.x<=20) {
               ;//G_eyepos.x += changes.x;
               G_scenetransl.x += changes.x;
            }
            if(changes.y<=20) {
               ;//G_eyepos.y -= changes.y;
               G_scenetransl.y -= changes.y;
            }
            prev_mouse_x = x;
            prev_mouse_y = y;
            break;
      }
   }
}

